﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Serialization;
using Cos.Core.Exception;
using Cos.Core.Service;
using Cos.Module.CMS;
using Cos.Module.Configuration;
using Cos.Module.Template;

namespace Alogient.Cameleon.Sdk.Items
{
    [Serializable]
    [XmlRoot("Page")]
    [XmlType("Page")]
    public class CMPage
    {
        protected string navigateUrl = string.Empty;

        protected CMPage()
        {
        }

        /// <summary>
        /// Returns the page in the current culture
        /// </summary>
        /// <param name="navID">NavID of the page</param>
        public CMPage(int navID)
            : this(navID, NavigationController.GetCultureCode())
        {
        }

        /// <summary>
        /// Returns the page
        /// </summary>
        /// <param name="navID">NavID of the page</param>
        /// <param name="cultureCode">Culture of the page</param>
        public CMPage(int navID, string cultureCode)
        {
            if (navID == 0)
            {
                this.cultureCode = cultureCode;
                this.pageTitle = this.templateName = "Root";
                this.templateUrl = "Default.aspx";
                this.pageUrl = String.Format("{0}?CultureCode={1}", this.TemplateUrl, this.CultureCode);
                this.navigateUrl = String.Format("~/{0}?CultureCode={1}", this.TemplateUrl, this.CultureCode);
                this.pageNavEnd = false;
                this.Elements = new CMElementCollection();
            }
            else
            {

                var cultureService = ServiceLocator.Get<ICultureService>();
                var pageService = ServiceLocator.Get<IPageService>();

                try
                {
                    var page = pageService.GetPage(cultureService.GetCulture(cultureCode), navID);

                    if (page == null || !page.IsVisible(CMServices.IsLive))
                    {
                        throw new CMException(String.Format("Page doesn't exist [NavID={0}, CultureCode={1}]", navID, cultureCode));
                    }

                    this.LoadPage(page);

                    CMServices.AddCachedPage(navID, cultureCode, this);
                }
                catch (PageNotFoundException ex)
                {
                    throw new CMException(String.Format("Page doesn't exist [NavID={0}, CultureCode={1}]", navID, cultureCode), ex);
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the CMPage class.
        /// Return a page using the sqlDataReader info
        /// </summary>
        /// <param name="sqlDataReader">
        /// Values of the page
        /// </param>
        [Obsolete("Don't use it", true)]
        public CMPage(SqlDataReader sqlDataReader)
        {
            throw new NotImplementedException("Not implemented"); // TODO
        }

        internal CMPage(Cos.Core.Model.Content.Page page)
        {
            this.LoadPage(page);
        }

        protected void LoadPage(Cos.Core.Model.Content.Page page)
        {
            if (page == null)
            {
                return;
            }

            var templateService = ServiceLocator.Get<ITemplateService>();
            var pageService = ServiceLocator.Get<IPageService>();
            var nav = pageService.GetNav(page.NavId);

            this.navId = page.NavId;
            
            if (nav.ParentNavId.HasValue)
            {
                this.parentId = nav.ParentNavId.Value;
            }

            this.pageId = page.PageId;
            this.cultureCode = page.CultureCode;
            this.pageTitle = CMServices.IsLive ? page.PageName : page.PageNamePreview;
            this.pageNavEnd = page.PageNavEnd;

            if (nav.TemplateId.HasValue)
            {
                var template = templateService.GetTemplate(nav.TemplateId.Value);
                this.templateId = template.TemplateId;
                this.templateName = template.TemplateName;
                this.templateUrl = template.TemplateUrl;
            }

            this.author = page.Author;
            this.lastModified = page.LastModified.HasValue ? page.LastModified.Value : DateTime.Now;
            this.creationDate = page.CreationDate.HasValue ? page.CreationDate.Value : DateTime.Now;
            this.approbationDate = page.ApprobationDate.HasValue ? page.ApprobationDate.Value : DateTime.Now;
            this.pageUrl = String.Format("{0}?NavID={1}&CultureCode={2}", this.TemplateUrl, this.NavID, this.CultureCode);
            this.navigateUrl = String.Format("~/{0}?NavID={1}&CultureCode={2}", this.TemplateUrl, this.NavID, this.CultureCode);

            // TODO
            // var subPages = pageService.GetSubPages(this.PageID);
            // this.hasChildren = subPages.Count() > 0;

            // TODO
            // this.RewrittenUrl = CleanedRewrittedUrl(page.);
            this.RewrittenUrl = string.Empty;

            // TODO
            // this.HasAccess
            this.HasAccess = true;
        }

        protected string CleanedRewrittedUrl(string s)
        {
            s = s.Replace("index.aspx", string.Empty);

            s = UrlRewriting.Utils.RemoveDiacritics(s);

            s = string.Format("/{0}", s);

            return s;
        }

        [XmlAttribute]
        public int NavID
        {
            get { return this.navId; }
            set { }
        }
        protected int navId;

        [XmlAttribute]
        public int ParentID
        {
            get { return this.parentId; }
            set { }
        }
        protected int parentId;

        [XmlIgnore]
        public int PageID
        {
            get { return this.pageId; }
        }
        protected int pageId;
        
        [XmlAttribute]
        public string CultureCode
        {
            get { return this.cultureCode; }
            set { }
        }
        protected string cultureCode;

        [XmlAttribute]
        public string PageTitle
        {
            get { return this.pageTitle; }
            set { }
        }
        protected string pageTitle;

        [XmlAttribute]
        public string PageUrl
        {
            get { return this.pageUrl; }
            set { }
        }
        protected string pageUrl;
        
        /// <summary>
        /// Gets the page url in the format ~/TemplateUrl?NavID=0&CultureCode=X
        /// </summary>
        [XmlIgnore]
        public string NavigateUrl 
        {
            get
            {
                if (CMSettings.UrlRewritingEnabled)
                {
                    return this.RewrittenUrl;
                }

                return this.navigateUrl;
            }
        }

        /// <summary>
        /// Gets the page rewritten url in the format culture/parentPageName/.../PageName/index.aspx
        /// </summary>
        [XmlIgnore]
        public string RewrittenUrl { get; protected set; }
        
        [XmlAttribute]
        public bool PageNavEnd
        {
            get { return this.pageNavEnd; }
            set { }
        }
        protected bool pageNavEnd;

        [XmlIgnore]
        public bool PageIsInMenu 
        {
            get { return !this.PageNavEnd; }
        }

        [XmlAttribute]
        public DateTime CreationDate
        {
            get { return this.creationDate; }
            set { }
        }
        protected DateTime creationDate;

        [XmlIgnore]
        public DateTime ApprobationDate
        {
            get { return this.approbationDate; }
            set { }
        }
        protected DateTime approbationDate;
        
        [XmlAttribute]
        public DateTime LastModified
        {
            get { return this.lastModified; }
            set { }
        }
        protected DateTime lastModified;

        [XmlAttribute]
        public string Author
        {
            get { return this.author; }
            set { }
        }
        protected string author;

        [XmlAttribute]
        public bool HasChildren
        {
            get { return this.hasChildren; }
            set { }
        }
        protected bool hasChildren;

        [XmlIgnore]
        public bool HasAccess { get; internal set; }

        [XmlIgnore]
        public CMPage Parent
        {
            get
            {
                if (this.navId == 0)
                {
                    return null;
                }

                return CMServices.GetPage(this.ParentID, this.CultureCode);
            }
        }

        [XmlAttribute]
        public int TemplateID
        {
            get { return this.templateId; }
            set { }
        }
        protected int templateId; 

        [XmlAttribute]
        public string TemplateName
        {
            get { return this.templateName; }
            set { }
        }
        protected string templateName;

        [XmlAttribute]
        public string TemplateUrl
        {
            get { return this.templateUrl; }
            set { }
        }
        protected string templateUrl;

        [XmlIgnore]
        public int XmlDepth { get; set; }
        
        [XmlIgnore]
        public CMPageCollection Breadcrumbs
        {
            get
            {
                if (this.breadcrumb == null)
                {
                    var bc = new CMPageCollection();
                    var tmpParentID = this.ParentID;
                    bc.Add(this);

                    while (tmpParentID > 0)
                    {
                        var parentPage = CMServices.GetPage(this.ParentID, this.CultureCode);
                        bc.Insert(0, parentPage);
                        tmpParentID = parentPage.ParentID;
                    }

                    this.breadcrumb = bc;
                }

                return this.breadcrumb;
            }
        }
        protected CMPageCollection breadcrumb;

        [XmlArray(ElementName = "Elements")]
        [XmlArrayItem(ElementName = "Element", Type = typeof(CMElement))]
        public CMElementCollection Elements { 
            get
            {
                if (this.elements == null)
                {
                    var pageService = ServiceLocator.Get<IPageService>();

                    var elementList = new CMElementCollection();
                    var pageElements = pageService.GetElements(this.PageID);

                    elementList.AddRange(pageElements.Select(e => new CMElement(e)));

                    this.elements = elementList;
                }

                return this.elements;
            }

            set { }
        }
        protected CMElementCollection elements;
        
        [XmlIgnore]
        public CMPageCollection Subpages
        {
            get 
            {
                if (this.subpages == null)
                {
                    var cultureService = ServiceLocator.Get<ICultureService>();
                    var pageService = ServiceLocator.Get<IPageService>();

                    var sub = pageService.GetSubPages(cultureService.GetCulture(this.CultureCode), this.NavID).Where(p => p.IsVisible(CMServices.IsLive));

                    var collection = new CMPageCollection();

                    collection.AddRange(sub.Select(p => new CMPage(p)));

                    this.subpages = collection;
                }

                return this.subpages;
            }
        }
        protected CMPageCollection subpages;

        [XmlArray(ElementName = "Subpages")]
        [XmlArrayItem(ElementName = "Page", Type = typeof(CMPage))]
        public CMPageCollection CMSubpages
        {
            get 
            {
                if (this.XmlDepth <= 0)
                {
                    return new CMPageCollection();
                }
                
                for (int i = 0; i < this.Subpages.Count; i++)
                {
                    this.Subpages[i].XmlDepth = this.XmlDepth - 1;
                }

                return this.Subpages;
            }

            set { } // Needed for the webservice
        }

        /// <summary>
        /// Returns the page title
        /// </summary>
        /// <returns>Page Title</returns>
        public string GetPageTitle()
        {
            return this.PageTitle;
        }
        
        /// <summary>
        /// Returns the value of an element of the page
        /// </summary>
        /// <param name="elementName">Element name</param>
        /// <returns>Element value</returns>
        public string GetElement(string elementName)
        {
            if (!string.IsNullOrEmpty(elementName))
            {
                foreach (CMElement cmElement in this.Elements)
                {
                    if (cmElement.ElementName == elementName)
                        return cmElement.Value;
                }
            }

            throw new CMException(String.Format("Cannot get Element - page doesn't contain such element [NavID={0}, CultureCode={1}, ElementName={2}]", this.NavID, this.CultureCode, elementName));
        }

        /// <summary>
        /// Returns the path to a media element of the page
        /// </summary>
        /// <param name="elementName">Element name</param>
        /// <returns>Path to the media</returns>
        public string GetMedia(string elementName)
        {
            if (!string.IsNullOrEmpty(elementName))
            {
                foreach (CMElement cmElement in this.Elements)
                {
                    if (cmElement.ElementName == elementName)
                    {
                        return cmElement.GetMediaFile();
                    }
                }
            }

            throw new CMException(String.Format("Cannot get Media - page doesn't contain such element [NavID={0}, CultureCode={1}, ElementName={2}]", this.NavID, this.CultureCode, elementName));
        }

        /// <summary>
        /// Returns the path to an image element of the page in its original format
        /// </summary>
        /// <param name="elementName">Element name</param>
        /// <returns>Path to the image</returns>
        public string GetImage(string elementName)
        {
            return this.GetImage(elementName, ImageFileType.Original);
        }

        /// <summary>
        /// Returns the path to an image element of the page
        /// </summary>
        /// <param name="elementName">Element name</param>
        /// <param name="imageFileType">Image format</param>
        /// <returns>Path to the image</returns>
        public string GetImage(string elementName, ImageFileType imageFileType)
        {
            if (!string.IsNullOrEmpty(elementName))
            {
                foreach (CMElement cmElement in this.Elements)
                {
                    if (cmElement.ElementName == elementName)
                    {
                        return cmElement.GetImageFile(imageFileType);
                    }
                }
            }

            throw new CMException(String.Format("Cannot get Image - page doesn't contain such element [NavID={0}, CultureCode={1}, ElementName={2}]", this.NavID, this.CultureCode, elementName));
        }

        /// <summary>
        /// Returns the page associated with the element of the page
        /// </summary>
        /// <param name="elementName">element name</param>
        /// <returns>A CMPage or null</returns>
        public CMPage GetAssociation(string elementName)
        {
            string strNav = this.Elements[elementName].Value;
            if (!string.IsNullOrEmpty(strNav))
            {
                int navID = 0;
                int.TryParse(strNav, out navID);
                if (navID > 0)
                {
                    return new CMPage(navID, this.CultureCode);
                }
            }

            return null;
        }

        /// <summary>
        /// Returns the pages associated with the element of the page.
        /// </summary>
        /// <param name="elementName">
        /// element name
        /// </param>
        /// <returns>
        /// The pages associated with the element of the page.
        /// </returns>
        public CMPageCollection GetMultiAssociation(string elementName)
        {
            const char separator = ';';
            var pages = new CMPageCollection();
            string strNavIDList = this.Elements[elementName].Value;

            if (!string.IsNullOrEmpty(strNavIDList))
            {
                string[] unEncodeValue = strNavIDList.Split(separator);

                foreach (string strNavID in unEncodeValue)
                {
                    int navID = 0;
                    if (int.TryParse(strNavID, out navID))
                    {
                        if (CMServices.PageExists(navID))
                        {
                            pages.Add(new CMPage(navID));
                        }
                    }
                }
            }

            return pages;
        }

        /// <summary>
        /// Get the width of an image element of the page (the original size)
        /// </summary>
        /// <param name="elementName">Element name</param>
        /// <returns>Image Width</returns>
        public int GetImageWidth(string elementName)
        {
            return this.GetImageWidth(elementName, ImageFileType.Original);
        }

        /// <summary>
        /// Get the width of an image element of the page (the defined size)
        /// </summary>
        /// <param name="elementName">Element name</param>
        /// <param name="imageFileType">Image format</param>
        /// <returns>Image Width</returns>
        public int GetImageWidth(string elementName, ImageFileType imageFileType)
        {
            if (!string.IsNullOrEmpty(elementName))
            {
                foreach (CMElement cmElement in this.Elements)
                {
                    if (cmElement.ElementName == elementName)
                    {
                        return cmElement.GetImageWidth(imageFileType);
                    }
                }
            }

            throw new CMException(String.Format("Cannot get Image Width - page doesn't contain such element [NavID={0}, CultureCode={1}, ElementName={2}]", this.NavID, this.CultureCode, elementName));
        }

        /// <summary>
        /// Get the height of an image element of the page (the original size)
        /// </summary>
        /// <param name="elementName">Element name</param>
        /// <returns>Image Height</returns>
        public int GetImageHeight(string elementName)
        {
            return this.GetImageHeight(elementName, ImageFileType.Original);
        }

        /// <summary>
        /// Get the height of an image element of the page (the defined size)
        /// </summary>
        /// <param name="elementName">Element name</param>
        /// <param name="imageFileType">Image format</param>
        /// <returns>Image Height</returns>
        public int GetImageHeight(string elementName, ImageFileType imageFileType)
        {
            if (!string.IsNullOrEmpty(elementName))
            {
                foreach (CMElement cmElement in this.Elements)
                {
                    if (cmElement.ElementName == elementName)
                    {
                        return cmElement.GetImageHeight(imageFileType);
                    }
                }
            }

            throw new CMException(String.Format("Cannot get Image Height - page doesn't contain such element [NavID={0}, CultureCode={1}, ElementName={2}]", this.NavID, this.CultureCode, elementName));
        }

        /// <summary>
        /// Returns the value of a property of the page. 
        /// (NavID, ParentID, CultureCode, PageTitle, PageNavEnd, TemplateID, TemplateName, TemplateUrl, PageUrl, Author, CreationDate, LastModified)
        /// </summary>
        /// <param name="propertyName">Property name</param>
        /// <returns>Property value</returns>
        [Obsolete("Don't use it", true)]
        public string GetProperty(string propertyName)
        {
            return string.Empty;
        }
    }
}
